iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
DevOps

《30天挑戰精通 PowerShell:從 Windows Server 到 Azure DevOps 自動化之旅》系列 第 29

Day 29 - 將 CI/CD 流程透過 Azure DevOps 去執行吧 - Part 3

  • 分享至 

  • xImage
  •  

經過前兩天的初步建置,我們終於可以開始設置 Pipeline 了!這次的目標是使用 Agent 建立前端靜態資源,並將它們推送到 Azure Pipeline Artifact。


概念圖

今天的內容聚焦在下圖中紅色框框所示的部分:
https://ithelp.ithome.com.tw/upload/images/20241013/201687086zwwB2WHX9.png


在 Azure DevOps 建立 Pipeline

建立空白 Pipeline

1. 進入 Azure DevOps Pipeline 頁面

在左側導航欄點擊 Pipeline。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708DqZBfb4Tzs.png

2. 點擊 Create Pipeline

點擊頁面中央的藍色按鈕「Create Pipeline」。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708viaCdsahPu.png

3. 選擇 classic editor

該模式能夠提供入門者更好的體驗,因為它能透過圖形化介面去編輯 Pipeline 裡的 Job,但是也能在編輯完後,透過閱讀 Pipeline 的 YAML 了解如何編輯 YAML,但是在功能上,在使用 classic editor 時,有可能會遇到部分功能無法使用,像是 Stage 與 Stage 之間的變數傳遞無法互通等問題,不過我們就先略過這件事吧,這次示範的專案並不需要用到那些功能。

直接選擇最下方的 classic editor。
https://ithelp.ithome.com.tw/upload/images/20241013/201687086s6cU4CVmW.png

4. 選擇 Source

選擇 Azure Repos Git 並點擊 Continue,我們將使用兩天前推送的 repo。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708AcclAln9uX.png

5. 選擇 Empty job

不選擇 template,直接使用「Empty job」。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708DLkh2R0bFa.png

選擇自託管的 Agent Pool

在 Tasks 裡的 Pipeline 階層可以選擇 Agent Pool,選擇昨天我們建立的 Agent。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708oph0mAOJN2.png

Get sources 裡的清理原則

在 Tasks 中的 Get sources 可以選擇預設的分支,並設定清理選項,以確保每次運行都是在乾淨的環境中進行。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708wMZ8rCPQhC.png

Clean options

預設是 Clean: false,但是可依據需求設定,其說明如下:

  1. Sources: 清理源碼目錄,確保每次構建時源碼目錄都是乾淨的。
  2. Sources and output directory: 清理源碼及輸出資料夾,適用於需要保證輸出資料也不影響的情況。
  3. Sources directory: 僅針對源碼進行清理。
  4. All build directories: 清理所有構建相關的資料夾,適合需要完全乾淨環境的情況。

依據 Package.json 建立個別的 Step

每個專案的任務需求不同,以下將根據專案的 package.json 和 Azure DevOps classic editor 的截圖做示範,但細節不多贅述,稍後會分享兩項重點:
Step 1: 暫時停用的「Capture Tag Version」,目的是擷取標籤版本以提升識別度。
Step 5: 「Publish Pipeline Artifact」,用於管理和推送生成的資源。

pipeline screenshot

這邊也將稍後 YAML 來源的階層位置一併標示。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708Jrr8iUcjVC.png

package.json

{
  "name": "code",
  "version": "0.0.1",
  "description": "enhance site reliability engineering",
  "productName": "Example Page",
  "author": "",
  "private": true,
  "scripts": {
    "lint": "eslint --ext .js,.vue ./",
    "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
    "test": "echo \"No test specified\" && exit 0"
  },
  "dependencies": {
    "@quasar/extras": "^1.0.0",
    "axios": "^1.2.1",
    "quasar": "^2.12.6",
    "vue": "^3.0.0",
    "vue-i18n": "^9.0.0",
    "vue-router": "^4.0.0",
    "vuex": "^4.1.0"
  },
  "devDependencies": {
    "@intlify/vite-plugin-vue-i18n": "^3.3.1",
    "@quasar/app-vite": "^1.0.0",
    "@quasar/cli": "^1.4.0",
    "autoprefixer": "^10.4.2",
    "eslint": "^8.10.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-vue": "^9.0.0",
    "postcss": "^8.4.14",
    "prettier": "^2.5.1"
  },
  "engines": {
    "node": "^18 || ^16 || ^14.19",
    "npm": ">= 6.13.4",
    "yarn": ">= 1.21.1"
  }
}

pipeline yaml ( scope - agent job )

pool:
  name: 30dayPool
steps:
- powershell: |
   $tag_version=("$(Build.SourceBranch)" -replace 'refs/tags/(qat-','')
   echo "##vso[build.updatebuildnumber]$tag_version"
   write-host "tag:$tag_version"
  displayName: 'Capture Tag Version'
  enabled: false

- task: NodeTool@0
  displayName: 'Use Node 16.x'
  inputs:
    versionSpec: 16.x
    checkLatest: true

- task: Npm@1
  displayName: 'npm install'
  inputs:
    workingDir: '$(Build.SourcesDirectory)'
    verbose: false

- bash: 'npx quasar build'
  workingDirectory: '$(Build.SourcesDirectory)'
  displayName: 'Run Quasar build'

- task: PublishPipelineArtifact@1
  displayName: 'Publish Pipeline Artifact'
  inputs:
    targetPath: '$(Build.SourcesDirectory)/dist'
    artifact: 'quasar-dist'

運行 Pipeline

手動觸發

手動觸發時,可以依據 branch or tag,以目前的截圖來說明,這次運行 Pipeline 時,它所使用的 source repo 將會是在 public_purpose branch 裡的最新版本。

點擊「Run pipeline」的右下角按鈕,並啟用 system diagnostics,以便排查問題。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708mQwNJFOkK6.png

自動觸發

預設下,在 Pipeline 裡的 Triggers 子頁籤裡,「 Enable continuous integration 」的選項並不會被勾選。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708X5CjuYdWc8.png

可以透過勾選該選項,使當該 branch 有新的 version 時,就自動跑該 CI,勾選完後,點擊 Save。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708i1sZodBMIE.png

Artifacts

這個專案裡我所使用的 artifact 是 pipeline artifact,而還有另外兩種在 Azure DevOps 裡,我這邊擷取網路上的文章,將資訊整理如下:

比較表

Build Artifacts Pipeline Artifacts Azure Artifacts
Older Newer Different Service
Classic and YAML YAML Classic and YAML
Slow Fast Fast
Tied to a pipeline run Tied to a pipeline run Independent
Can trigger CD Can trigger CD Can trigger CD
Cannot be shared Cannot be shared Shareable with teams, organizations, publicly
Can store anything Can store anything Typed packages
Free Free 2GB free, then paid

個別說明

Build Artifacts

  • 是較為傳統的一種工件儲存方式,原本是用來儲存從 build 中產生的輸出(如編譯好的應用程式)。
  • 它是與某個 pipeline 執行綁定的,並且可以作為下游釋出管道的觸發條件。
  • 無法分享給其他管道或專案使用,更多是短期的保存方式。

Pipeline Artifacts

  • Pipeline Artifacts 是一種較新的方式,可以讓 YAML 定義更靈活並改善效率。
  • 它也與 pipeline 執行綁定,並且比 build artifacts 更快。
  • 雖然也能夠觸發下游的 CI/CD 流程,但無法被不同的 pipeline 共享。

Azure Artifacts

  • Azure Artifacts 是一個完全不同的服務,可以幫助管理和分享工件,比如 NuGet、npm、Maven 這些包管理工具。
  • 它是完全獨立的,不需要依賴某個 pipeline,可以與不同團隊、組織甚至公開共享。
  • 支援類型化的包管理,例如打包工具生成的包,並且有特定的免費使用限制(2GB 以內免費,之後需付費)。

使 pipeline artifact 具有識別度

細看 Step 5 的 YAML 設定

steps:
- task: PublishPipelineArtifact@1
  displayName: 'Publish Pipeline Artifact'
  inputs:
    targetPath: '$(Build.SourcesDirectory)/dist'
    artifact: 'quasar-dist'

targetPath 很好理解,將 agent build folder 裡透過 quasar build 出來放在 dist folder 裡的靜態資源 push 上去 pipeline artifact,但是看看第二個屬性,artifact,他的值「 quasar-dist 」並不能具有識別目前這個 artifact 是屬於哪個 version,假設,今天 CI 跑了兩個 version,但是 CD 在執行上有時間差時,你實際上無法判斷目前 CD 吃到的到底是哪個 CI 所產生的 pipeline artifact,因此需要讓它具有識別度的值,因此,就會講到剛剛被 disabled 那個 step 1。

透過 PowerShell 擷取 tag 裡的 version

細看 Step 1 的 PowerShell script

$tag_version = "$(Build.SourceBranch)" -replace 'refs/tags/qat-', ''
echo "##vso[build.updatebuildnumber]$tag_version"
write-host "tag: $tag_version"
  • $(Build.SourceBranch) 是 Azure DevOps 提供的一個系統變數,通常用於表示當前 pipeline 觸發的 Git 分支或標籤名稱。
  • "##vso[build.updatebuildnumber]$tag\_version" 是 Azure DevOps 的一個特殊命令,用於通知 Azure DevOps 代理更改當前 pipeline 的 build number。

這段 PowerShell 腳本的主要目的是從 Build.SourceBranch 中提取出 qat- 之後的版本號,然後將其作為當前 Azure DevOps pipeline 的 build number,並且在控制台中顯示提取的 tag 版本號。這樣做可以使構建版本更具可追溯性並與 Git tag 保持一致,接著,我們就可以將該 version 用於 artifact 值內,但是前提是,每次的 CI 都是由 git tag 來 trigger 並且其內容要符合其規範。

Trigger CI 的規範

原有設定是將 Enable continuous integration 打開,使 branch 有新 version 時自動跑 pipeline。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708BpDUU34e3H.png

我們將 trigger 的條件限縮以符合我們希望每次 trigger pipeline 的 tag 都能夠使 step 1 的 powershell 抓取到 version,並將其使用於 artifact 裡,因此我們可以透過 Branch filters 裡,透過點擊 Branch specification 的下拉 button,並將其值規範成:

refs/tags/qat-*

這樣每次 CI 都只會被符合規範的 tag 所 trigger( 當然,代表 * 的 version 其格式要另行自主規範 )。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708VZQUxAWc04.png

做完後,我們將其 step 1 給 enable 起來。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708NIfDuvTPvm.png

修改 step 5 裡的 artifact 值,將其值搭配 build number。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708FEr37vGVfh.png

透過在 Azure Repos 裡寫入一筆符合其規範的 tag 後,CI 會自動開始跑,並呈現在 repo 的 commit 頁面上。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708hTB4SwjkXE.png

並在 pipeline 運行的 log 看到成功抓取 tag version 為 1.0.1。
https://ithelp.ithome.com.tw/upload/images/20241013/201687081QLJ5nKzE8.png

而 step 5 裡也能看到其 name 已經加上了 version。
https://ithelp.ithome.com.tw/upload/images/20241013/20168708OtX8Dg00Nh.png

到此,整個 CI 流程跑完,而我們 build 的 static resources 也被放進 pipeline artifact 裡,等著我們明天透過 CD 去使用它,明天見。


明日主題

Day 30 - 將 CI/CD 流程透過 Azure DevOps 去執行吧 - 完結。


上一篇
Day 28 - 將 CI/CD 流程透過 Azure DevOps 去執行吧 - Part 2
下一篇
Day 30 - 將 CI/CD 流程透過 Azure DevOps 去執行吧 - 完結。
系列文
《30天挑戰精通 PowerShell:從 Windows Server 到 Azure DevOps 自動化之旅》30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言